/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

const PENDING = "pending"; 
const FULFILLED = "fulfilled"; 
const REJECTED = "rejected"; 

class MyPromise {
    status = PENDING
    value = undefined
    reason = undefined
    successCallback = []
    failCallback = []

    constructor (exctor) {
        try {
            exctor(this.resolve ,this.reject)
        } catch(e) {
            this.reject(e)
        }
    }

    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED
        this.value = value
        while (this.successCallback.length) this.successCallback.shift()()
    }

    reject = reason => {
        if (this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason
        while (this.failCallback.length) this.failCallback.shift()()
    }

    then (successCallback ,failCallback) {

        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : reason => {throw reason}

       let promise2 = new MyPromise((resolve ,reject) => {
        if (this.status === FULFILLED) {
           setTimeout(() => {
            try {
                let result = successCallback(this.value)
                resolvePromise(promise2,result,resolve,reject)
            } catch(e) {
                reject(e)
            }
           }, 0);

        } else if (this.status === REJECTED) {
            setTimeout(() => {
                try {
                    let result = failCallback(this.reason)
                    resolvePromise(promise2, result, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            }, 0)
        } else {
            this.successCallback.push(() => {
                setTimeout(() => {
                 try {
                    let result = successCallback(this.value)
                    resolvePromise(promise2,result,resolve,reject)
                 } catch(e) {
                     reject(e)
                 }
                }, 0);
            })
            this.failCallback.push(() => {
               setTimeout(() => {
                try {
                    let result = failCallback(this.reason)
                resolvePromise(promise2,result,resolve,reject)
                } catch(e) {
                    reject(e)
                }
               }, 0);
            })
        }
       })
       return promise2
    }


    static all (array) {
        let result = []
        let index = 0
        return new MyPromise((resolve ,reject) => {

             let addData = (key,value) => {
                result[key] = value
                index++

                if (index === array.length) {
                    resolve(result)
                }
            }
            
            for (let i=0;i<array.length;i++) {
                if (array[i] instanceof MyPromise) {
                    array[i].then(value => {
                        addData(i,value)
                    },reason => reject(reason))
                }else {
                    addData(i,array[i])
                }
            }
        })
    }

    static resolve(value) {
        if (value instanceof MyPromise) return value

        return new MyPromise((resolve => resolve(value)))
    }

    finally (callback) {
        return this.then(value => {
           return MyPromise.resolve(callback()).then(() =>value)
        },reason => {
            return MyPromise.resolve(callback()).then(() =>{throw reason})
        })
    }

    catch(failCallback) {
        return this.then(undefined,failCallback)
      }
}

function resolvePromise(promise2,result, resolve, reject) {
    if (result === promise2) {
        return reject(
            new TypeError("Chaining cycle detected for promise #<Promise>")
          );
    }
    if (result instanceof MyPromise) {
        result.then(resolve,reject)
    } else {
        resolve(result)
    }
}

module.exports = MyPromise